using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Globalization;
using EastBancTech.InstallerDefinition.Native;

namespace EastBancTech.InstallerDefinition
{
    public partial class SetupDefinitionDirectiveProcessor
    {
        private const string requiresBaseSourceDirParameter = "BaseSourceDir";
        private const string requiresFileParameter = "FileName";
        private bool oneTimeCodeGenerated;

        public override void StartProcessingRun(CodeDomProvider languageProvider, string templateContents, CompilerErrorCollection errors)
        {
            base.StartProcessingRun(languageProvider, templateContents, errors);
            oneTimeCodeGenerated = false;
        }

        protected override void InitializeProvidesDictionary(string directiveName, IDictionary<string, string> providesDictionary)
        {
            base.InitializeProvidesDictionary(directiveName, providesDictionary);

            providesDictionary[requiresBaseSourceDirParameter] = base.Host.ResolveParameterValue(directiveName, "SetupDefinitionDirectiveProcessor", requiresBaseSourceDirParameter);
        }
        
        protected override void GenerateTransformCode(string directiveName, StringBuilder codeBuffer, System.CodeDom.Compiler.CodeDomProvider languageProvider, IDictionary<string, string> requiresArguments, IDictionary<string, string> providesArguments)
        {
            base.GenerateTransformCode(directiveName, codeBuffer, languageProvider, requiresArguments, providesArguments);

            if ((StringComparer.InvariantCultureIgnoreCase.Compare(directiveName, "SetupDefinition") == 0) && (languageProvider != null))
            {
                CodeTypeReference guidTypeReference = new CodeTypeReference(typeof(Guid));
                CodeTypeReference stringTypeReference = new CodeTypeReference(typeof(string));

                CodeMemberMethod method1 = new CodeMemberMethod();
                method1.Name = "GenerateComponentGuid";
                method1.ReturnType = guidTypeReference;
                method1.Attributes = MemberAttributes.Private;
                method1.Parameters.Add(new CodeParameterDeclarationExpression(guidTypeReference, "namespaceGuid"));
                method1.Parameters.Add(new CodeParameterDeclarationExpression(stringTypeReference, "name"));
                method1.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(GuidFromName)), "Generate"), new CodeExpression[] { new CodeVariableReferenceExpression(method1.Parameters[0].Name), new CodeVariableReferenceExpression(method1.Parameters[1].Name) })));
                CodeMemberMethod method2 = new CodeMemberMethod();
                method2.Name = "GenerateComponentGuid";
                method2.ReturnType = guidTypeReference;
                method2.Attributes = MemberAttributes.Private;
                method2.Parameters.Add(new CodeParameterDeclarationExpression(stringTypeReference, "name"));
                method2.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(GuidFromName)), "Generate"), new CodeExpression[] { new CodeVariableReferenceExpression(method2.Parameters[0].Name) })));
                CodeMemberMethod method3 = new CodeMemberMethod();
                method3.Name = "GetShortFileName";
                method3.ReturnType = stringTypeReference;
                method3.Attributes = MemberAttributes.Public;
                method3.Parameters.Add(new CodeParameterDeclarationExpression(stringTypeReference, "longName"));
                method3.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(FileSystemInterop)), "GetShortFileName"), new CodeExpression[] {new System.CodeDom.CodePropertyReferenceExpression(new System.CodeDom.CodeThisReferenceExpression(), "Store"), new CodeVariableReferenceExpression(method3.Parameters[0].Name) })));

                // Create a helper method for resolving relative paths
                global::System.CodeDom.CodeMemberMethod resolver = null;
                if (!this.oneTimeCodeGenerated)
                {
                    resolver = new global::System.CodeDom.CodeMemberMethod();
                    resolver.Name = "Convert" + "PathToRelativePath";
                    resolver.ReturnType = new global::System.CodeDom.CodeTypeReference(typeof(string));
                    resolver.Parameters.Add(new global::System.CodeDom.CodeParameterDeclarationExpression(typeof(string), "path"));
                    resolver.Attributes = global::System.CodeDom.MemberAttributes.Public;
                    global::System.CodeDom.CodeVariableDeclarationStatement declaration = null;

                    if (requiresArguments.ContainsKey(requiresBaseSourceDirParameter))
                        declaration = new global::System.CodeDom.CodeVariableDeclarationStatement(typeof(string), "basePath", new global::System.CodeDom.CodePrimitiveExpression((string)(Path.GetDirectoryName(requiresArguments[requiresBaseSourceDirParameter]))));
                    else
                        declaration = new global::System.CodeDom.CodeVariableDeclarationStatement(typeof(string), "basePath", new global::System.CodeDom.CodePrimitiveExpression((string)(Path.GetDirectoryName(requiresArguments[requiresFileParameter]))));
                    resolver.Statements.Add(declaration);
                    
                    global::System.CodeDom.CodeMethodReturnStatement returnStatement = new global::System.CodeDom.CodeMethodReturnStatement(
                        new global::System.CodeDom.CodeMethodInvokeExpression(
                            new global::System.CodeDom.CodeMethodReferenceExpression(new global::System.CodeDom.CodeTypeReferenceExpression("EastBancTech.DSL.FileSystemDefinition.Native.FileSystemInterop"), "GetRelativePath"),
                            new global::System.CodeDom.CodeVariableReferenceExpression("path"),
                            new global::System.CodeDom.CodeVariableReferenceExpression("basePath")));
                    resolver.Statements.Add(returnStatement);
        
                }

                List<System.CodeDom.CodeMemberProperty> properties = new List<CodeMemberProperty>();

                foreach (String argName in providesArguments.Keys)
                {
                    // Create a property for the Model that delay-loads the model
					System.CodeDom.CodeMemberProperty installerPackageModelProperty = new System.CodeDom.CodeMemberProperty();
                    installerPackageModelProperty.Name = argName;
					installerPackageModelProperty.Type = new System.CodeDom.CodeTypeReference(typeof(String));
					installerPackageModelProperty.Attributes = System.CodeDom.MemberAttributes.Public;
					installerPackageModelProperty.HasSet = false;
					installerPackageModelProperty.HasGet = true;
                    installerPackageModelProperty.GetStatements.Add(new CodeMethodReturnStatement(new System.CodeDom.CodePrimitiveExpression(providesArguments[argName])));
                    properties.Add(installerPackageModelProperty);
                }

                                
                CodeGeneratorOptions options1 = new CodeGeneratorOptions();
                options1.BlankLinesBetweenMembers = true;
                options1.IndentString = "\t";
                options1.VerbatimOrder = true;
                options1.BracingStyle = "C";
                using (StringWriter writer1 = new StringWriter(codeBuffer, CultureInfo.InvariantCulture))
                {
                    languageProvider.GenerateCodeFromMember(method1, writer1, options1);
                    languageProvider.GenerateCodeFromMember(method2, writer1, options1);
                    languageProvider.GenerateCodeFromMember(method3, writer1, options1);

                    if (resolver != null)
                        languageProvider.GenerateCodeFromMember(resolver, writer1, options1);

                    foreach(System.CodeDom.CodeMemberProperty p in properties)
                        languageProvider.GenerateCodeFromMember(p, writer1, options1);
                }

                oneTimeCodeGenerated = true;
            }
        }

        protected override void GeneratePostInitializationCode(string directiveName, StringBuilder codeBuffer, CodeDomProvider languageProvider, IDictionary<string, string> requiresArguments, IDictionary<string, string> providesArguments)
        {
            base.GeneratePostInitializationCode(directiveName, codeBuffer, languageProvider, requiresArguments, providesArguments);

            if ((StringComparer.InvariantCultureIgnoreCase.Compare(directiveName, "SetupDefinition") == 0) && (languageProvider != null))
            {
                CodeMethodInvokeExpression code = new CodeMethodInvokeExpression(
                    new System.CodeDom.CodePropertyReferenceExpression(
                    new System.CodeDom.CodePropertyReferenceExpression(
                    new System.CodeDom.CodeThisReferenceExpression(), "Store"), "PropertyBag"), "Add", new System.CodeDom.CodePrimitiveExpression("modelFilePath"),
                    new global::System.CodeDom.CodePrimitiveExpression(requiresArguments["FileName"]));
                 //store.PropertyBag.set_Item("modelFilePath", inputFile)

                CodeMethodInvokeExpression code1 = new CodeMethodInvokeExpression(
                    new System.CodeDom.CodePropertyReferenceExpression(
                    new System.CodeDom.CodePropertyReferenceExpression(
                    new System.CodeDom.CodeThisReferenceExpression(), "Store"), "PropertyBag"), "Add", new System.CodeDom.CodePrimitiveExpression(requiresBaseSourceDirParameter),
                    new global::System.CodeDom.CodePrimitiveExpression(String.IsNullOrEmpty(providesArguments[requiresBaseSourceDirParameter]) ? Path.GetDirectoryName(requiresArguments["FileName"]) : providesArguments[requiresBaseSourceDirParameter]));

                CodeGeneratorOptions options1 = new CodeGeneratorOptions();
                options1.BlankLinesBetweenMembers = true;
                options1.IndentString = "\t";
                options1.VerbatimOrder = true;
                options1.BracingStyle = "C";

                using (StringWriter writer1 = new StringWriter(codeBuffer, CultureInfo.InvariantCulture))
                {
                    languageProvider.GenerateCodeFromStatement(new System.CodeDom.CodeExpressionStatement(code), writer1, options1);
                    languageProvider.GenerateCodeFromStatement(new System.CodeDom.CodeExpressionStatement(code1), writer1, options1);
                }
            }
        }
    }
}
